/*-
 * Written by Atsushi Murai <amurai@spec.co.jp>
 * Copyright (c) 1998, System Planning and Engineering Co.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *  TODO:
 *       oClean up.
 *       oConsidering for word alignment for other platform.
 */

#ifndef VBOX
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/sys/netinet/libalias/alias_nbt.c,v 1.20.8.1 2009/04/15 03:14:26 kensmith Exp $");

/*
    alias_nbt.c performs special processing for NetBios over TCP/IP
    sessions by UDP.

    Initial version:  May, 1998  (Atsushi Murai <amurai@spec.co.jp>)

    See HISTORY file for record of revisions.
*/

/* Includes */
#ifdef _KERNEL
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#else
#include <errno.h>
#include <sys/types.h>
#include <stdio.h>
#endif

#include <netinet/in_systm.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/udp.h>

#ifdef _KERNEL
#include <netinet/libalias/alias_local.h>
#include <netinet/libalias/alias_mod.h>
#else
#include "alias_local.h"
#include "alias_mod.h"
#endif
#else  /* VBOX */
# include <iprt/ctype.h>
# include <slirp.h>
# include "alias_local.h"
# include "alias_mod.h"
# define isprint RT_C_IS_PRINT
#endif /* VBOX */

#define NETBIOS_NS_PORT_NUMBER 137
#define NETBIOS_DGM_PORT_NUMBER 138

static int
AliasHandleUdpNbt(struct libalias *, struct ip *, struct alias_link *,
          struct in_addr *, u_short);

static int
AliasHandleUdpNbtNS(struct libalias *, struct ip *, struct alias_link *,
            struct in_addr *, u_short *, struct in_addr *, u_short *);
static int
fingerprint1(struct libalias *la, struct ip *pip, struct alias_data *ah)
{

#ifdef VBOX
    NOREF(la);
    NOREF(pip);
#endif
    if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL ||
        ah->aaddr == NULL || ah->aport == NULL)
        return (-1);
    if (ntohs(*ah->dport) == NETBIOS_DGM_PORT_NUMBER
        || ntohs(*ah->sport) == NETBIOS_DGM_PORT_NUMBER)
        return (0);
    return (-1);
}

static int
protohandler1(struct libalias *la, struct ip *pip, struct alias_data *ah)
{

    AliasHandleUdpNbt(la, pip, ah->lnk, ah->aaddr, *ah->aport);
    return (0);
}

static int
fingerprint2(struct libalias *la, struct ip *pip, struct alias_data *ah)
{

#ifdef VBOX
    NOREF(la);
    NOREF(pip);
#endif
    if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL ||
        ah->aaddr == NULL || ah->aport == NULL)
        return (-1);
    if (ntohs(*ah->dport) == NETBIOS_NS_PORT_NUMBER
        || ntohs(*ah->sport) == NETBIOS_NS_PORT_NUMBER)
        return (0);
    return (-1);
}

static int
protohandler2in(struct libalias *la, struct ip *pip, struct alias_data *ah)
{

    AliasHandleUdpNbtNS(la, pip, ah->lnk, ah->aaddr, ah->aport,
                ah->oaddr, ah->dport);
    return (0);
}

static int
protohandler2out(struct libalias *la, struct ip *pip, struct alias_data *ah)
{

    AliasHandleUdpNbtNS(la, pip, ah->lnk, &pip->ip_src, ah->sport,
                ah->aaddr, ah->aport);
    return (0);
}

/* Kernel module definition. */
#ifndef VBOX
struct proto_handler handlers[] = {
    {
      .pri = 130,
      .dir = IN|OUT,
      .proto = UDP,
      .fingerprint = &fingerprint1,
      .protohandler = &protohandler1
    },
    {
      .pri = 140,
      .dir = IN,
      .proto = UDP,
      .fingerprint = &fingerprint2,
      .protohandler = &protohandler2in
    },
    {
      .pri = 140,
      .dir = OUT,
      .proto = UDP,
      .fingerprint = &fingerprint2,
      .protohandler = &protohandler2out
    },
    { EOH }
};
#else  /* VBOX */
#define handlers pData->nbt_module
#endif /* VBOX */

#ifndef VBOX
static int
mod_handler(module_t mod, int type, void *data)
#else  /* VBOX */
static int nbt_alias_handler(PNATState pData, int type);

int
nbt_alias_load(PNATState pData)
{
    return nbt_alias_handler(pData, MOD_LOAD);
}

int
nbt_alias_unload(PNATState pData)
{
    return nbt_alias_handler(pData, MOD_UNLOAD);
}
static int
nbt_alias_handler(PNATState pData, int type)
#endif /* VBOX */
{
    int error;
#ifdef VBOX
    if (handlers == NULL)
        handlers = RTMemAllocZ(4 * sizeof(struct proto_handler));
    handlers[0].pri = 130;
    handlers[0].dir = IN|OUT;
    handlers[0].proto = UDP;
    handlers[0].fingerprint = &fingerprint1;
    handlers[0].protohandler = &protohandler1;


    handlers[1].pri = 140;
    handlers[1].dir = IN;
    handlers[1].proto = UDP;
    handlers[1].fingerprint = &fingerprint2;
    handlers[1].protohandler = &protohandler2in;


    handlers[2].pri = 140;
    handlers[2].dir = OUT;
    handlers[2].proto = UDP;
    handlers[2].fingerprint = &fingerprint2;
    handlers[2].protohandler = &protohandler2out;

    handlers[3].pri = (u_int)EOH;
#endif /* VBOX */

    switch (type) {
    case MOD_LOAD:
        error = 0;
#ifdef VBOX
        LibAliasAttachHandlers(pData, handlers);
#else
        LibAliasAttachHandlers(handlers);
#endif
        break;
    case MOD_UNLOAD:
        error = 0;
#ifdef VBOX
        LibAliasDetachHandlers(pData, handlers);
        RTMemFree(handlers);
        handlers = NULL;
#else
        LibAliasDetachHandlers(handlers);
#endif
        break;
    default:
        error = EINVAL;
    }
    return (error);
}

#ifndef VBOX
#ifdef  _KERNEL
static
#endif
moduledata_t alias_mod = {
       "alias_nbt", mod_handler, NULL
};
#endif /* !VBOX */

#ifdef  _KERNEL
DECLARE_MODULE(alias_nbt, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
MODULE_VERSION(alias_nbt, 1);
MODULE_DEPEND(alias_nbt, libalias, 1, 1, 1);
#endif

typedef struct {
    struct in_addr  oldaddr;
    u_short     oldport;
    struct in_addr  newaddr;
    u_short     newport;
    u_short        *uh_sum;
}       NBTArguments;

typedef struct {
    unsigned char   type;
    unsigned char   flags;
    u_short     id;
    struct in_addr  source_ip;
    u_short     source_port;
    u_short     len;
    u_short     offset;
}       NbtDataHeader;

#define OpQuery     0
#define OpUnknown   4
#define OpRegist    5
#define OpRelease   6
#define OpWACK      7
#define OpRefresh   8
typedef struct {
#ifndef VBOX
    u_short     nametrid;
    u_short     dir:    1, opcode:4, nmflags:7, rcode:4;
#else
    unsigned    nametrid:16;
    unsigned    dir:    1, opcode:4, nmflags:7, rcode:4;
#endif
    u_short     qdcount;
    u_short     ancount;
    u_short     nscount;
    u_short     arcount;
}       NbtNSHeader;
AssertCompileSize(NbtNSHeader, 12);

#define FMT_ERR     0x1
#define SRV_ERR     0x2
#define IMP_ERR     0x4
#define RFS_ERR     0x5
#define ACT_ERR     0x6
#define CFT_ERR     0x7


#ifdef LIBALIAS_DEBUG
static void
PrintRcode(u_char rcode)
{

    switch (rcode) {
        case FMT_ERR:
        printf("\nFormat Error.");
    case SRV_ERR:
        printf("\nSever failure.");
    case IMP_ERR:
        printf("\nUnsupported request error.\n");
    case RFS_ERR:
        printf("\nRefused error.\n");
    case ACT_ERR:
        printf("\nActive error.\n");
    case CFT_ERR:
        printf("\nName in conflict error.\n");
    default:
        printf("\n?%c?=%0x\n", '?', rcode);

    }
}

#endif


/* Handling Name field */
static u_char  *
AliasHandleName(u_char * p, char *pmax)
{

    u_char *s;
#ifdef LIBALIAS_DEBUG
    u_char c;
#endif
    int compress;

    /* Following length field */

    if (p == NULL || (char *)p >= pmax)
        return (NULL);

    if (*p & 0xc0) {
        p = p + 2;
        if ((char *)p > pmax)
            return (NULL);
        return ((u_char *) p);
    }
    while ((*p & 0x3f) != 0x00) {
        s = p + 1;
        if (*p == 0x20)
            compress = 1;
        else
            compress = 0;

        /* Get next length field */
        p = (u_char *) (p + (*p & 0x3f) + 1);
        if ((char *)p > pmax) {
            p = NULL;
            break;
        }
#ifdef LIBALIAS_DEBUG
        printf(":");
#endif
        while (s < p) {
            if (compress == 1) {
#ifdef LIBALIAS_DEBUG
                c = (u_char) (((((*s & 0x0f) << 4) | (*(s + 1) & 0x0f)) - 0x11));
                if (isprint(c))
                    printf("%c", c);
                else
                    printf("<0x%02x>", c);
#endif
                s += 2;
            } else {
#ifdef LIBALIAS_DEBUG
                printf("%c", *s);
#endif
                s++;
            }
        }
#ifdef LIBALIAS_DEBUG
        printf(":");
        fflush(stdout);
#endif
    }

    /* Set up to out of Name field */
    if (p == NULL || (char *)p >= pmax)
        p = NULL;
    else
        p++;
    return ((u_char *) p);
}

/*
 * NetBios Datagram Handler (IP/UDP)
 */
#define DGM_DIRECT_UNIQ     0x10
#define DGM_DIRECT_GROUP    0x11
#define DGM_BROADCAST       0x12
#define DGM_ERROR           0x13
#define DGM_QUERY           0x14
#define DGM_POSITIVE_RES    0x15
#define DGM_NEGATIVE_RES    0x16

static int
AliasHandleUdpNbt(
    struct libalias *la,
    struct ip *pip,     /* IP packet to examine/patch */
    struct alias_link *lnk,
    struct in_addr *alias_address,
    u_short alias_port
)
{
    struct udphdr *uh;
    NbtDataHeader *ndh;
    u_char *p = NULL;
    char *pmax;

    (void)la;
    (void)lnk;

    /* Calculate data length of UDP packet */
    uh = (struct udphdr *)ip_next(pip);
    pmax = (char *)uh + ntohs(uh->uh_ulen);

    /* IP header has been verified, cross-check uh_ulen */
    if (RT_UNLIKELY(pmax != (char *)pip + ntohs(pip->ip_len)))
        return (-1);

    ndh = (NbtDataHeader *)udp_next(uh);
    if ((char *)(ndh + 1) > pmax)
        return (-1);
#ifdef LIBALIAS_DEBUG
    printf("\nType=%02x,", ndh->type);
#endif
    switch (ndh->type) {
    case DGM_DIRECT_UNIQ:
    case DGM_DIRECT_GROUP:
    case DGM_BROADCAST:
        p = (u_char *) ndh + 14;
        p = AliasHandleName(p, pmax);   /* Source Name */
        p = AliasHandleName(p, pmax);   /* Destination Name */
        break;
    case DGM_ERROR:
        p = (u_char *) ndh + 11;
        break;
    case DGM_QUERY:
    case DGM_POSITIVE_RES:
    case DGM_NEGATIVE_RES:
        p = (u_char *) ndh + 10;
        p = AliasHandleName(p, pmax);   /* Destination Name */
        break;
    }
    if (p == NULL || (char *)p > pmax)
        p = NULL;
#ifdef LIBALIAS_DEBUG
    printf("%s:%d-->", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port));
#endif
    /* Doing an IP address and Port number Translation */
    if (uh->uh_sum != 0) {
        int acc;
        u_short *sptr;

        acc = ndh->source_port;
        acc -= alias_port;
        sptr = (u_short *) & (ndh->source_ip);
        acc += *sptr++;
        acc += *sptr;
        sptr = (u_short *) alias_address;
        acc -= *sptr++;
        acc -= *sptr;
        ADJUST_CHECKSUM(acc, uh->uh_sum);
    }
    ndh->source_ip = *alias_address;
    ndh->source_port = alias_port;
#ifdef LIBALIAS_DEBUG
    printf("%s:%d\n", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port));
    fflush(stdout);
#endif
    return ((p == NULL) ? -1 : 0);
}

/* Question Section */
#define QS_TYPE_NB      0x0020
#define QS_TYPE_NBSTAT  0x0021
#define QS_CLAS_IN      0x0001
typedef struct {
    u_short     type;   /* The type of Request */
    u_short     class;  /* The class of Request */
}       NBTNsQuestion;

static u_char  *
AliasHandleQuestion(
    u_short count,
    NBTNsQuestion * q,
    char *pmax,
    NBTArguments * nbtarg)
{

    (void)nbtarg;

    while (count != 0) {
        /* Name Filed */
        q = (NBTNsQuestion *) AliasHandleName((u_char *) q, pmax);

        if (q == NULL || (char *)(q + 1) > pmax) {
            q = NULL;
            break;
        }
        /* Type and Class filed */
        switch (ntohs(q->type)) {
        case QS_TYPE_NB:
        case QS_TYPE_NBSTAT:
            q = q + 1;
            break;
        default:
#ifdef LIBALIAS_DEBUG
            printf("\nUnknown Type on Question %0x\n", ntohs(q->type));
#endif
            break;
        }
        count--;
    }

    /* Set up to out of Question Section */
    return ((u_char *) q);
}

/* Resource Record */
#define RR_TYPE_A       0x0001
#define RR_TYPE_NS      0x0002
#define RR_TYPE_NULL    0x000a
#define RR_TYPE_NB      0x0020
#define RR_TYPE_NBSTAT  0x0021
#define RR_CLAS_IN      0x0001
#define SizeOfNsResource    8
typedef struct {
    u_short     type;
    u_short     class;
    unsigned int    ttl;
    u_short     rdlen;
}       NBTNsResource;

#define SizeOfNsRNB         6
typedef struct {
#ifndef VBOX
    u_short     g:  1  , ont:2, resv:13;
#else
    unsigned    g:  1  , ont:2, resv:13;
#endif
    struct in_addr  addr;
}       NBTNsRNB;
AssertCompileSize(NBTNsRNB, 8);

static u_char  *
AliasHandleResourceNB(
    NBTNsResource * q,
    char *pmax,
    NBTArguments * nbtarg)
{
    NBTNsRNB *nb;
    u_short bcount;

    if (q == NULL || (char *)(q + 1) > pmax)
        return (NULL);
    /* Check out a length */
    bcount = ntohs(q->rdlen);

    /* Forward to Resource NB position */
    nb = (NBTNsRNB *) ((u_char *) q + SizeOfNsResource);

    /* Processing all in_addr array */
#ifdef LIBALIAS_DEBUG
    printf("NB rec[%s", inet_ntoa(nbtarg->oldaddr));
    printf("->%s, %dbytes] ", inet_ntoa(nbtarg->newaddr), bcount);
#endif
    while (nb != NULL && bcount != 0) {
        if ((char *)(nb + 1) > pmax) {
            nb = NULL;
            break;
        }
#ifdef LIBALIAS_DEBUG
        printf("<%s>", inet_ntoa(nb->addr));
#endif
        if (!bcmp(&nbtarg->oldaddr, &nb->addr, sizeof(struct in_addr))) {
            if (*nbtarg->uh_sum != 0) {
                int acc;
                u_short *sptr;

                sptr = (u_short *) & (nb->addr);
                acc = *sptr++;
                acc += *sptr;
                sptr = (u_short *) & (nbtarg->newaddr);
                acc -= *sptr++;
                acc -= *sptr;
                ADJUST_CHECKSUM(acc, *nbtarg->uh_sum);
            }
            nb->addr = nbtarg->newaddr;
#ifdef LIBALIAS_DEBUG
            printf("O");
#endif
        }
#ifdef LIBALIAS_DEBUG
        else {
            printf(".");
        }
#endif
        nb = (NBTNsRNB *) ((u_char *) nb + SizeOfNsRNB);
        bcount -= SizeOfNsRNB;
    }
    if (nb == NULL || (char *)(nb + 1) > pmax) {
        nb = NULL;
    }
    return ((u_char *) nb);
}

#define SizeOfResourceA     6
typedef struct {
    struct in_addr  addr;
}       NBTNsResourceA;

static u_char  *
AliasHandleResourceA(
    NBTNsResource * q,
    char *pmax,
    NBTArguments * nbtarg)
{
    NBTNsResourceA *a;
    u_short bcount;

    if (q == NULL || (char *)(q + 1) > pmax)
        return (NULL);

    /* Forward to Resource A position */
    a = (NBTNsResourceA *) ((u_char *) q + sizeof(NBTNsResource));

    /* Check out of length */
    bcount = ntohs(q->rdlen);

    /* Processing all in_addr array */
#ifdef LIBALIAS_DEBUG
    printf("Arec [%s", inet_ntoa(nbtarg->oldaddr));
    printf("->%s]", inet_ntoa(nbtarg->newaddr));
#endif
    while (bcount != 0) {
        if (a == NULL || (char *)(a + 1) > pmax)
            return (NULL);
#ifdef LIBALIAS_DEBUG
        printf("..%s", inet_ntoa(a->addr));
#endif
        if (!bcmp(&nbtarg->oldaddr, &a->addr, sizeof(struct in_addr))) {
            if (*nbtarg->uh_sum != 0) {
                int acc;
                u_short *sptr;

                sptr = (u_short *) & (a->addr); /* Old */
                acc = *sptr++;
                acc += *sptr;
                sptr = (u_short *) & nbtarg->newaddr;   /* New */
                acc -= *sptr++;
                acc -= *sptr;
                ADJUST_CHECKSUM(acc, *nbtarg->uh_sum);
            }
            a->addr = nbtarg->newaddr;
        }
        a++;        /* XXXX */
        bcount -= SizeOfResourceA;
    }
    if (a == NULL || (char *)(a + 1) > pmax)
        a = NULL;
    return ((u_char *) a);
}

typedef struct {
#ifndef VBOX
    u_short     opcode:4, flags:8, resv:4;
#else
    u_short     hidden; /* obviously not needed */
#endif
}       NBTNsResourceNULL;
AssertCompileSize(NBTNsResourceNULL, 2);

static u_char  *
AliasHandleResourceNULL(
    NBTNsResource * q,
    char *pmax,
    NBTArguments * nbtarg)
{
    NBTNsResourceNULL *n;
    u_short bcount;

    (void)nbtarg;

    if (q == NULL || (char *)(q + 1) > pmax)
        return (NULL);

    /* Forward to Resource NULL position */
    n = (NBTNsResourceNULL *) ((u_char *) q + sizeof(NBTNsResource));

    /* Check out of length */
    bcount = ntohs(q->rdlen);

    /* Processing all in_addr array */
    while (bcount != 0) {
        if ((char *)(n + 1) > pmax) {
            n = NULL;
            break;
        }
        n++;
        bcount -= sizeof(NBTNsResourceNULL);
    }
    if ((char *)(n + 1) > pmax)
        n = NULL;

    return ((u_char *) n);
}

static u_char  *
AliasHandleResourceNS(
    NBTNsResource * q,
    char *pmax,
    NBTArguments * nbtarg)
{
    NBTNsResourceNULL *n;
    u_short bcount;

    (void)nbtarg;

    if (q == NULL || (char *)(q + 1) > pmax)
        return (NULL);

    /* Forward to Resource NULL position */
    n = (NBTNsResourceNULL *) ((u_char *) q + sizeof(NBTNsResource));

    /* Check out of length */
    bcount = ntohs(q->rdlen);

    /* Resource Record Name Filed */
    q = (NBTNsResource *) AliasHandleName((u_char *) n, pmax);  /* XXX */

    if (q == NULL || (char *)((u_char *) n + bcount) > pmax)
        return (NULL);
    else
        return ((u_char *) n + bcount);
}

typedef struct {
    u_short     numnames;
}       NBTNsResourceNBSTAT;

static u_char  *
AliasHandleResourceNBSTAT(
    NBTNsResource * q,
    char *pmax,
    NBTArguments * nbtarg)
{
    NBTNsResourceNBSTAT *n;
    u_short bcount;

    (void)nbtarg;

    if (q == NULL || (char *)(q + 1) > pmax)
        return (NULL);

    /* Forward to Resource NBSTAT position */
    n = (NBTNsResourceNBSTAT *) ((u_char *) q + sizeof(NBTNsResource));

    /* Check out of length */
    bcount = ntohs(q->rdlen);

    if ((char *)((u_char *) n + bcount) > pmax)
        return (NULL);
    else
        return ((u_char *) n + bcount);
}

static u_char  *
AliasHandleResource(
    u_short count,
    NBTNsResource * q,
    char *pmax,
    NBTArguments
    * nbtarg)
{
    while (count != 0) {
        /* Resource Record Name Filed */
        q = (NBTNsResource *) AliasHandleName((u_char *) q, pmax);

        if (q == NULL || (char *)(q + 1) > pmax)
            break;
#ifdef LIBALIAS_DEBUG
        printf("type=%02x, count=%d\n", ntohs(q->type), count);
#endif

        /* Type and Class filed */
        switch (ntohs(q->type)) {
        case RR_TYPE_NB:
            q = (NBTNsResource *) AliasHandleResourceNB(
                q,
                pmax,
                nbtarg
                );
            break;
        case RR_TYPE_A:
            q = (NBTNsResource *) AliasHandleResourceA(
                q,
                pmax,
                nbtarg
                );
            break;
        case RR_TYPE_NS:
            q = (NBTNsResource *) AliasHandleResourceNS(
                q,
                pmax,
                nbtarg
                );
            break;
        case RR_TYPE_NULL:
            q = (NBTNsResource *) AliasHandleResourceNULL(
                q,
                pmax,
                nbtarg
                );
            break;
        case RR_TYPE_NBSTAT:
            q = (NBTNsResource *) AliasHandleResourceNBSTAT(
                q,
                pmax,
                nbtarg
                );
            break;
        default:
#ifdef LIBALIAS_DEBUG
            printf(
                "\nUnknown Type of Resource %0x\n",
                ntohs(q->type)
                );
            fflush(stdout);
#endif
            break;
        }
        count--;
    }
    return ((u_char *) q);
}

static int
AliasHandleUdpNbtNS(
    struct libalias *la,
    struct ip *pip,     /* IP packet to examine/patch */
    struct alias_link *lnk,
    struct in_addr *alias_address,
    u_short * alias_port,
    struct in_addr *original_address,
    u_short * original_port)
{
    struct udphdr *uh;
    NbtNSHeader *nsh;
    u_char *p;
    char *pmax;
    NBTArguments nbtarg;

    (void)la;
    (void)lnk;

    /* Set up Common Parameter */
    nbtarg.oldaddr = *alias_address;
    nbtarg.oldport = *alias_port;
    nbtarg.newaddr = *original_address;
    nbtarg.newport = *original_port;

    /* Calculate data length of UDP packet */
    uh = (struct udphdr *)ip_next(pip);
    nbtarg.uh_sum = &(uh->uh_sum);
    nsh = (NbtNSHeader *)udp_next(uh);
    p = (u_char *) (nsh + 1);
    pmax = (char *)uh + ntohs(uh->uh_ulen);

    /* IP header has been verified, cross-check uh_ulen */
    if (RT_UNLIKELY(pmax != (char *)pip + ntohs(pip->ip_len)))
        return (-1);

    if ((char *)(nsh + 1) > pmax)
        return (-1);

#ifdef LIBALIAS_DEBUG
    printf(" [%s] ID=%02x, op=%01x, flag=%02x, rcode=%01x, qd=%04x"
        ", an=%04x, ns=%04x, ar=%04x, [%d]-->",
        nsh->dir ? "Response" : "Request",
        nsh->nametrid,
        nsh->opcode,
        nsh->nmflags,
        nsh->rcode,
        ntohs(nsh->qdcount),
        ntohs(nsh->ancount),
        ntohs(nsh->nscount),
        ntohs(nsh->arcount),
        (u_char *) p - (u_char *) nsh
        );
#endif

    /* Question Entries */
    if (ntohs(nsh->qdcount) != 0) {
        p = AliasHandleQuestion(
            ntohs(nsh->qdcount),
            (NBTNsQuestion *) p,
            pmax,
            &nbtarg
            );
    }
    /* Answer Resource Records */
    if (ntohs(nsh->ancount) != 0) {
        p = AliasHandleResource(
            ntohs(nsh->ancount),
            (NBTNsResource *) p,
            pmax,
            &nbtarg
            );
    }
    /* Authority Resource Recodrs */
    if (ntohs(nsh->nscount) != 0) {
        p = AliasHandleResource(
            ntohs(nsh->nscount),
            (NBTNsResource *) p,
            pmax,
            &nbtarg
            );
    }
    /* Additional Resource Recodrs */
    if (ntohs(nsh->arcount) != 0) {
        p = AliasHandleResource(
            ntohs(nsh->arcount),
            (NBTNsResource *) p,
            pmax,
            &nbtarg
            );
    }
#ifdef LIBALIAS_DEBUG
    PrintRcode(nsh->rcode);
#endif
    return ((p == NULL) ? -1 : 0);
}
